package br.ufc.great.dtx.tm;

import java.io.Serializable;
import java.util.Vector;

import br.ufc.great.client.DTXInsertOperation;
import br.ufc.great.client.DTXOperation;
import br.ufc.great.client.DTXQueryOperation;
import br.ufc.great.client.DTXResult;
import br.ufc.great.client.DTXSite;
import br.ufc.great.client.DTXTransaction;
import br.ufc.great.dataguide.DGElement;
import br.ufc.great.dtx.tm.operations.InsertOperation;
import br.ufc.great.dtx.tm.operations.Operation;
import br.ufc.great.dtx.tm.operations.QueryOperation;
import br.ufc.great.dtx.tm.operations.Site;
import br.ufc.great.xml.XMLElement;

/**
 * 
 * @author Leonardo Oliveira Moreira
 *
 * Classe utilizada para representar uma transação
 */
public class Transaction implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 8390518324573525030L;
	
	public static final int NONE = 1;
	public static final int ABORTED = 2;
	public static final int COMMITED = 4;

	private String id;
	private Vector<Operation> operations;
	private int status;
	private int operationSequence;
	
	private TransactionManagerThread thread;
	
	private boolean abort;
	
	public Transaction(String id) {
		this.id = id;
		abort = false;
		status = NONE;
		operationSequence = 0;
		operations = new Vector<Operation>();
	}

	public String getId() {
		return id;
	}

	public Vector<Operation> getOperations() {
		return operations;
	}
	
	public Operation nextOperation() throws AbortTransactionException, CommitTransactionException {
		for (int i = 0; i < operations.size(); i++) {
			Operation operation = operations.elementAt(i);
			switch (operation.getState()) {
				case Operation.STATE_NONE : {
					return operation;
				}
				case Operation.STATE_ABORTED: {
					throw new AbortTransactionException();
				}
			}
		}
		throw new CommitTransactionException();
	}
	
	public int getStatus() {
		return status;
	}

	public void commit() {
		status = COMMITED;
	}
	
	public void abort() {
		status = ABORTED;
	}
	
	public boolean isEnded() {
		return (status == COMMITED || status == ABORTED);
	}
	
	public void addQueryOperation(Vector<Site> sites, String query) {
		QueryOperation operation = new QueryOperation(String.valueOf(++operationSequence), getId());
		operation.setSites(sites);
		operation.setQuery(query);
		operations.add(operation);
	}
	
	public void addInsertOperation(Vector<Site> sites, int type, String query, XMLElement constructor) {
		InsertOperation operation = new InsertOperation(String.valueOf(++operationSequence), getId());
		operation.setSites(sites);
		operation.setInsertType(type);
		operation.setQuery(query);
		operation.setConstructor(constructor);
		operations.add(operation);
	}
	
	public static Transaction toTransaction(DTXTransaction dtxTransaction) {
		Transaction transaction = new Transaction(dtxTransaction.getId());
		Vector<DTXOperation> dtxOperations = dtxTransaction.getOperations();
		for (int i = 0; i < dtxOperations.size(); i++) {
			DTXOperation dtxOperation = dtxOperations.elementAt(i);
			Vector<Site> sites = new Vector<Site>();
			Vector<DTXSite> dtxSites = dtxOperation.getSites();
			for (int j = 0; j < dtxSites.size(); j++) {
				DTXSite dtxSite = dtxSites.elementAt(j);
				sites.add(new Site(dtxSite.getHost(), dtxSite.getPort()));
			}
			switch (dtxOperation.getType()) {
				case DTXOperation.QUERY : {
					transaction.addQueryOperation(sites, ((DTXQueryOperation) dtxOperation).getQuery());
					break;
				}
				case DTXOperation.INSERT : {
					transaction.addInsertOperation(sites, ((DTXInsertOperation) dtxOperation).getInsertType(), ((DTXInsertOperation) dtxOperation).getQuery(), ((DTXInsertOperation) dtxOperation).getConstructor());
					break;
				}
			}
		}
		return transaction;
	}
	
	public static DTXResult transactionToDTXResult(Transaction transaction) {
		Vector<Operation> operations = transaction.getOperations();
		String[] results = new String[operations.size()];
		for (int i = 0; i < operations.size(); i++) {
			Operation operation = operations.elementAt(i);
			results[i] = operation.getResult();
		}
		DTXResult result = new DTXResult(transaction.getId(), results);		
		switch (transaction.getStatus()) {
			case Transaction.ABORTED : {
				result.setStatus(DTXResult.ABORTED);
				break;
			}
			case Transaction.COMMITED : {
				result.setStatus(DTXResult.COMMITED);
				break;
			}
		}
		return result;
	}

	public TransactionManagerThread getThread() {
		return thread;
	}

	public void setThread(TransactionManagerThread thread) {
		this.thread = thread;
	}

	public boolean isAbort() {
		return abort;
	}

	public void setAbort(boolean abort) {
		this.abort = abort;
	}
		
}
